/*
    This file is part of the Time-of-Flight Tracer program. It is not part of
    the original PBRT source distribution. See the included license file for
    more information.

    Copyright(c) 2014 Microsoft Corporation

    Author: Phil Pitts
*/

#include "stdafx.h"
#include "film/backscatter/signal.h"


SignalFilm::SignalFilm(int xres, int yres, const string &fn, 
	vector<float> &freqs, vector<float> &phs) : BackscatterFilm(xres, yres, 
	fn) {

	frequencies = freqs;
	phases = phs;
	nPhases = phases.size();
	nFreq = frequencies.size();

	pixels = new BlockedArray<volatile float**>(xres, yres);
	for (int x = 0; x < xres; x++) {
		for (int y = 0; y < yres; y++) {
			volatile float** &pixel = (*pixels)(x, y);
			pixel = new volatile float*[nFreq];
			for (int i = 0; i < nFreq; i++) {
				pixel[i] = new volatile float[nPhases];
				for (int j = 0; j < nPhases; j++) {
					pixel[i][j] = 0.f;
				}
			}
		}
	}
}


SignalFilm::~SignalFilm() {
	for (int x = 0; x < xResolution; x++) {
		for (int y = 0; y < yResolution; y++) {
			volatile float** &pixel = (*pixels)(x, y);
			for (int i = 0; i < nFreq; i++) {
				delete[] pixel[i];
			}
			delete[] pixel;
		}
	}
	delete pixels;
}


void SignalFilm::AddSample(const CameraSample &sample, const PaintData &paint) {
	Splat(sample, paint);
}


void SignalFilm::Splat(const CameraSample &sample, const PaintData &paint) {
	int x = Floor2Int(sample.imageX), y = Floor2Int(sample.imageY);
	// Address floating point error for values close to pixel resolution
	// Ex. 99.999999 --> (Error) --> 100.000000 --> (Floor) --> 100
	if (x == xResolution) x--;
	if (y == yResolution) y--;
	volatile float** &pixel = (*pixels)(x, y);

	for (int i = 0; i < nFreq; i++) {
		for (int j = 0; j < nPhases; j++) {
			for (int k = 0; k < paint.bounces.size(); k++) {
				float kernel = GetKernel(frequencies[i], phases[j],
					paint.bounces[i].distance);
				float contrib = paint.bounces[i].radiance * kernel;
				AtomicAdd(&pixel[i][j], contrib);
			}
		}
	}
}


void SignalFilm::WriteImage(float splatScale) {
	FILE* fp = fopen(filename.c_str(), "w");
	if (!fp) Severe("Film file %s could not be opened", filename);

	for (int x = 0; x < xResolution; x++) {
		for (int y = 0; y < yResolution; y++) {
			volatile float** &pixel = (*pixels)(x, y);
			for (int i = 0; i < nFreq; i++) {
				for (int j = 0; j < nPhases; j++) {
					fprintf(fp, "%f ", pixel[i][j]);
				}
			}
		}
	}
	fclose(fp);
}


float SignalFilm::GetKernel(float frequency, float phase, float distance) {
	return cos(4 * M_PI * frequency * distance / SPEED_LIGHT + phase);
}


SignalFilm *CreateSignalFilm(const ParamSet &params) {
	int xres, yres;
	string filename;
	GetBackscatterParams(params, &xres, &yres, &filename);

	int nFreq, nPhases;
	const float *freqArray, *phaseArray;
	freqArray = params.FindFloat("frequencies", &nFreq);
	phaseArray = params.FindFloat("phases", &nPhases);

	vector<float> frequencies(nFreq);
	vector<float> phases(nPhases);
	for (int i = 0; i < nFreq; i++) frequencies[i] = freqArray[i];
	for (int i = 0; i < nPhases; i++) phases[i] = phaseArray[i];

	return new SignalFilm(xres, yres, filename, frequencies, phases);
}